<template>
  <div class="fx-dropdown" ref="dropdownDom" @click="dropdownClick" @mouseenter="dropdownEnter"
       @mouseleave="dropdownLeave">
    <slot name="header"></slot>
    <transition @before-enter="animationBeforeEnter" @enter="animationEnter" @leave="animationLeave">
      <ul class="fx-dropdown-list" :class="{top:isToTop}" ref="dropdownListDom" v-if="dropdownListShow">
        <slot name="default"></slot>
      </ul>
    </transition>
  </div>
</template>

<script lang="ts">
import { defineComponent, onBeforeUnmount, onMounted, PropType, provide, ref, SetupContext } from 'vue'
import { getZIndex, useClickBlank } from '@/base/hooks'
import mitt, { Emitter } from 'mitt'
import { prefixStyle } from '@/base/utils'

const Transform = prefixStyle('transform')

interface DropdownProps {
  isClickClose: boolean;
  trigger: 'hover' | 'click';
}

export default defineComponent({
  name: 'FDropdown',
  props: {
    // 是否需要点击隐藏
    isClickClose: {
      type: Boolean as PropType<boolean>,
      default: true
    },
    // 触发方式
    trigger: {
      type: String as PropType<'hover' | 'click'>,
      default: 'click',
      validator (val: string): boolean {
        return ['hover', 'click'].includes(val)
      }
    }
  },
  emits: ['change'],
  setup (props: DropdownProps, ctx: SetupContext) {
    const dropdownMitt: Emitter = mitt()
    const dropdownDom = ref<HTMLElement>()
    const dropdownListDom = ref<HTMLElement>()
    const dropdownListShow = ref<boolean>(false)
    const isToTop = ref<boolean>(false)
    useClickBlank(dropdownDom, dropdownListShow)

    provide<Emitter['emit']>('dropdownMittEmit', dropdownMitt.emit)
    provide<boolean>('isClickClose', props.isClickClose)

    const updateDropdown = () => {
      if (!dropdownListShow.value) return
      if (!dropdownDom.value || !dropdownListDom.value) return
      const rect = dropdownDom.value?.getBoundingClientRect()
      const listHeight = dropdownListDom.value.children.length * 32 > 200 ? 200 : dropdownListDom.value.children.length * 32
      const toBottom = rect.top + rect.height + 10
      const toTop = rect.top - listHeight - 10
      const isOver = (toBottom + listHeight) > document.documentElement.clientHeight
      isToTop.value = isOver
      const top = !isOver ? toBottom : toTop
      dropdownListDom.value.style.left = `${rect.left + rect.width / 2}px`
      dropdownListDom.value.style.top = `${top}px`
    }

    const dropdownClick = (): void => {
      if (props.trigger !== 'click') return
      dropdownListShow.value = !dropdownListShow.value
    }

    const dropdownEnter = (): void => {
      if (props.trigger !== 'hover') return
      dropdownListShow.value = true
    }

    const dropdownLeave = (): void => {
      if (props.trigger !== 'hover') return
      dropdownListShow.value = false
    }

    const handleDropdownItem = (val: string | number | undefined): void => {
      if (props.trigger === 'hover' && props.isClickClose) dropdownListShow.value = false
      ctx.emit('change', val)
    }

    const animationBeforeEnter = (el: HTMLElement): void => {
      if (!dropdownDom.value || !dropdownListShow.value) return
      const rect = dropdownDom.value?.getBoundingClientRect()
      const listHeight = el.children.length * 32 > 200 ? 200 : el.children.length * 32
      const toBottom = rect.top + rect.height + 10
      const toTop = rect.top - listHeight - 10
      const isOver = (toBottom + listHeight) > document.documentElement.clientHeight
      isToTop.value = isOver
      const top = !isOver ? toBottom : toTop
      el.style[Transform] = 'translateX(-50%) scaleY(0)'
      el.style.left = `${rect.left + rect.width / 2}px`
      el.style.top = `${top}px`
      el.style.zIndex = String(getZIndex())
    }

    const animationEnter = (el: HTMLElement): void => {
      setTimeout(() => {
        el.style[Transform] = 'translateX(-50%) scaleY(1)'
      })
    }

    const animationLeave = (el: HTMLElement) => {
      setTimeout(() => {
        el.style[Transform] = 'translateX(-50%) scaleY(0)'
      })
    }

    onMounted(() => {
      document.addEventListener('scroll', updateDropdown, true)
      dropdownMitt.on<string | number | undefined>('dropdownItemClick', handleDropdownItem)
    })

    onBeforeUnmount(() => {
      document.removeEventListener('scroll', updateDropdown, true)
      dropdownMitt.off('dropdownItemClick', handleDropdownItem)
    })

    return {
      isToTop,
      dropdownDom,
      dropdownListDom,
      dropdownListShow,
      updateDropdown,
      dropdownEnter,
      dropdownLeave,
      dropdownClick,
      animationLeave,
      animationBeforeEnter,
      animationEnter
    }
  }
})
</script>
